package com.syyo.admin.utils;

import com.syyo.admin.domain.JavaProperties;
import freemarker.cache.ClassTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * @Auther: wangzhong
 * @Date: 2020/8/22 16:14
 * @Description:
 */
@Slf4j
public class GeneratorUtils {

    /**
     * 简单的代码生成器.
     *
     * @param classFilePath  文件生成的路劲 java   G:\syyo\code\java\kaiyuan\syyoAdmin-server\generator\src\main\java\com\syyo\generator\controller\
     * @param templateName   模板文件的名称 controller.java.ftl
     * @param javaProperties 数据库的字段对象   字段的名称，类型，大小写，主键
     * @param fileName       生成的文件名 controller.java
     * @throws IOException       the io exception
     * @throws TemplateException the templates exception
     */
    public static void create(String classFilePath,
                              String templateName,
                              JavaProperties javaProperties,
                              String fileName) {

        // freemarker 配置
        Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
        configuration.setDefaultEncoding("UTF-8");
        // 指定模板的路径
        OutputStreamWriter outputStreamWriter = null;
        try {
            // 最后的路劲是：resources/generator
            configuration.setTemplateLoader(new ClassTemplateLoader(ClassLoaderUtils.getClassLoader(), "generator"));
            // 根据模板名称获取路径下的模板
            Template template = configuration.getTemplate(templateName);
            String out = "";
            if ("index.vue".equals(fileName)) {
                // 生成前端vue代码的文件路劲
                out = classFilePath + fileName;
            } else if ("api.js".equals(fileName)) {
                // 生成前端api代码的文件路劲
                out = classFilePath + javaProperties.getClassName() + ".js";
            } else {
                // 生成后端的文件路劲
                String upperFileName = toUpperFirstOne(fileName);
                String javaClassName = (javaProperties.getClassName() + upperFileName);
                out = classFilePath + javaClassName;
            }

            File dest = new File(out);
            // 文件不存在，创建
            if (!dest.getParentFile().exists()) {
                dest.getParentFile().mkdirs();
            }
            // 定义一个输出流来导出代码文件
            outputStreamWriter = new OutputStreamWriter(new FileOutputStream(out));
            // freemarker 引擎将动态数据绑定的模板并导出为文件
            template.process(javaProperties, outputStreamWriter);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("文件异常：", e);
        } finally {
            if (outputStreamWriter != null) {
                try {
                    outputStreamWriter.close();
                } catch (IOException e) {
                    e.printStackTrace();
                    log.error("文件异常：", e);
                }
            }
        }
    }

    /**
     * 预览代码
     *
     * @param templateName   模板文件的名称 controller.java.ftl
     * @param javaProperties 数据库的字段对象   字段的名称，类型，大小写，主键
     * @throws IOException       the io exception
     * @throws TemplateException the templates exception
     */
    private static String preview(String templateName,
                                  JavaProperties javaProperties) {

        // freemarker 配置
        Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
        configuration.setDefaultEncoding("UTF-8");
        // 指定模板的路径
        StringWriter writer = null;
        String content = "";
        try {
            // 获取当前线程的字节码对象 ClassLoaderUtils.getClassLoader()
            configuration.setTemplateLoader(new ClassTemplateLoader(ClassLoaderUtils.getClassLoader(), "generator"));
            // 根据模板名称获取路径下的模板
            Template template = configuration.getTemplate(templateName);
            writer = new StringWriter();
            template.process(javaProperties, writer);
            content = writer.toString();
        } catch (Exception e) {
            e.printStackTrace();
            log.error("文件异常：", e);
        } finally {
            if (writer != null) {
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                    log.error("文件异常：", e);
                }
            }
        }
        return content;
    }

    private static Map<String, Object> init(String packageName, String moduleNameFull, String moduleName) {
        // 当前项目的地址 G:\syyo\code\java\kaiyuan\syyoAdmin-server
        String rootPath = System.getProperty("user.dir");
        String sprit = File.separator;//获取系统的斜杠
        String packageNames = ""; //转成斜杠 包名  \com\syyo
        String[] split = packageName.split("\\.");
        for (String s : split) {
            packageNames = packageNames + sprit + s;
        }
        // 当前项目的地址
        String applicationPath = getApplicationPath(rootPath, moduleNameFull, moduleName, packageNames, sprit);
        // xml文件生成的路劲
        String resourcesPath = getResourcesPath(rootPath, moduleNameFull, moduleName, sprit);
        // 模板文件名
        List<String> templates = getTemplates();

        Map<String, Object> map = new HashMap<>();
        map.put("resourcesPath", resourcesPath);
        map.put("applicationPath", applicationPath);
        map.put("templates", templates);
        map.put("sprit", sprit);
        return map;
    }

    private static String getResourcesPath(String rootPath, String moduleNameFull, String moduleName, String sprit) {
        // 模板（controller.java.ftl）的路劲
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append(rootPath);
        stringBuffer.append(sprit);
        stringBuffer.append(moduleNameFull);
        stringBuffer.append(sprit);
        stringBuffer.append("src");
        stringBuffer.append(sprit);
        stringBuffer.append("main");
        stringBuffer.append(sprit);
        stringBuffer.append("resources");
        stringBuffer.append(sprit);
        stringBuffer.append("mapper");
        stringBuffer.append(sprit);
        stringBuffer.append(moduleName);
        stringBuffer.append(sprit);
        return stringBuffer.toString();
    }

    /**
     * 根据启动方式，获取对应的模板文件夹路劲
     */
    public static String getTemplatePath(String profilesActive) {
        String templatePath = "";
        // 由于是分模块+本地启动和jar包启动会有模板路劲找不到的问题，所以，分成本地启动的路劲和jar启动的路劲
        if ("dev".equals(profilesActive)) {
            // 本地启动
            templatePath = GeneratorUtils.class.getResource("/generator").getPath();
        } else {
            // jar包启动
            templatePath = System.getProperty("user.dir") + "/classes/templates/generator";
        }
        return templatePath;
    }

    /**
     * 获取当前启动类的路劲 G:\syyo\code\java\kaiyuan\syyoAdmin-server\admin\src\main\java\\com\syyo\admin\
     *
     * @param rootPath
     * @param moduleName
     * @param packageNames
     * @param sprit
     * @return
     */
    private static String getApplicationPath(String rootPath, String moduleNameFull, String moduleName, String packageNames, String sprit) {
        // 当前项目的地址
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append(rootPath);
        stringBuffer.append(sprit);
        stringBuffer.append(moduleNameFull);
        stringBuffer.append(sprit);
        stringBuffer.append("src");
        stringBuffer.append(sprit);
        stringBuffer.append("main");
        stringBuffer.append(sprit);
        stringBuffer.append("java");
        stringBuffer.append(packageNames);
        stringBuffer.append(sprit);
        stringBuffer.append(moduleName);
        stringBuffer.append(sprit);
        return stringBuffer.toString();
    }

    /**
     * 获取模板文件的名称
     *
     * @return
     */
    private static List<String> getTemplates() {
        List<String> list = new ArrayList<>();
        String reqPath = "req.java.ftl";
        String entityPath = "entity.java.ftl";
        String exportPath = "export.java.ftl";
        String controllerPath = "controller.java.ftl";
        String servicePath = "service.java.ftl";
        String serviceImplPath = "serviceImpl.java.ftl";
        String mapperPath = "mapper.java.ftl";
        String mapperXmlPath = "mapper.xml.ftl";
        String apiPath = "api.js.ftl";
        String indexPath = "index.vue.ftl";
        list.add(reqPath);
        list.add(entityPath);
        list.add(exportPath);
        list.add(controllerPath);
        list.add(servicePath);
        list.add(serviceImplPath);
        list.add(mapperPath);
        list.add(mapperXmlPath);
        list.add(apiPath);
        list.add(indexPath);

        // 获取当前目录下面的文件名,会排序混乱的问题
//        Set<String> templateFiles = new HashSet<>();
//        File file = new File(templatePath);
//        File[] array = file.listFiles();
//        for (File file1 : array) {
//            if (file1.isFile()){
//                // 获取所有的模板文件名
//                templateFiles.add(file1.getName());
//            }
//        }
        return list;

    }

    /**
     * 根据文件名拼接对应的是生成文件的路劲
     *
     * @param templateName    文件名 controller.java
     * @param applicationPath 启动类的路劲
     * @param sprit
     * @return
     */
    private static String isFileType(String templateName, String applicationPath, String sprit) {
        String controllerPath = applicationPath + "controller" + sprit;// controller 路劲
        String servicePath = applicationPath + "service" + sprit;// service 路劲
        String serviceImplPath = servicePath + "impl" + sprit;// impl 路劲
        String mapperPath = applicationPath + "mapper" + sprit;// mapper 路劲
        String mapperXmlPath = applicationPath + "mapper" + sprit;// mapper 路劲
        String entityPath = applicationPath + "domain" + sprit + "entity" + sprit;// entity 路劲
        String exportPath = applicationPath + "domain" + sprit + "export" + sprit;// entity 路劲
        String reqPath = applicationPath + "domain" + sprit + "req" + sprit;// req 路劲
        switch (templateName) {
            case "entity.java":
                return entityPath;
            case "req.java":
                return reqPath;
            case "export.java":
                return exportPath;
            case "controller.java":
                return controllerPath;
            case "service.java":
                return servicePath;
            case "serviceImpl.java":
                return serviceImplPath;
            case "mapper.xml":
                return mapperXmlPath;
            default:
                return mapperPath;
        }
    }

    /**
     * 将字符串的首字母转大写
     *
     * @param str 需要转换的字符串
     * @return
     */
    private static String toUpperFirstOne(String str) {
        // 进行字母的ascii编码前移，效率要高于截取字符串进行转换的操作
        char[] cs = str.toCharArray();
        cs[0] -= 32;
        return String.valueOf(cs);
    }

    /**
     * 生成模板
     *
     * @param apiPath 前端模板的生成路劲
     * @return
     */
    public static void createTemplates(JavaProperties userEntity, String classFilePath, Boolean cover, String apiPath) {
        String module = userEntity.getModule();//模块 "generator"
        String moduleFull = userEntity.getModuleFull();//模块 全名
        String pack = userEntity.getPack();//包名 "com.syyo"

        // 初始化，获取启动类路劲、模板路劲、模板文件名
        Map<String, Object> map = init(pack, moduleFull, module);
        String applicationPath = map.get("applicationPath").toString();
        String sprit = map.get("sprit").toString();
        List<String> templates = (List<String>) map.get("templates");

        if (MyStringUtils.isNotEmpty(classFilePath)) {
            // 去临时文件里生成
            for (String templateName : templates) {
                // 文件名
                String fileName = templateName.substring(0, templateName.lastIndexOf("."));
                // 打ZIP包
                create(classFilePath, templateName, userEntity, fileName);
            }
        } else {
            // 空路劲，去代码里的对应文件里生成
            for (String templateName : templates) {
                // 文件名
                String fileName = templateName.substring(0, templateName.lastIndexOf("."));
                if ("index.vue".equals(fileName) || "api.js".equals(fileName)) {
                    // 前端代码：生成到配置好的路劲
                    classFilePath = apiPath + File.separator;
                } else if ("mapper.xml".equals(fileName)) {
                    // 后端xml文件：生成到项目的对应路劲
                    classFilePath = map.get("resourcesPath").toString();
                    ;// 生成后的文件路劲
                } else {
                    // 后端代码：生成到项目的对应路劲
                    classFilePath = isFileType(fileName, applicationPath, sprit);// 生成后的文件路劲
                }

                // 后端代码：生成到项目的对应路劲
                File file = new File(classFilePath + fileName);
//                if (!cover && file.exists()) {
//                    // 不覆盖，并且文件存在就跳过
//                }
                if (cover) {
                    // 覆盖，生成新的文件
                    create(classFilePath, templateName, userEntity, fileName);

                }else {
                    if (!file.exists()) {
                        // 不覆盖，文件不存在，才生成新的文件
                        create(classFilePath, templateName, userEntity, fileName);
                    }
                }
            }
        }
    }

    /**
     * 预览模板
     *
     * @param
     * @return
     */
    public static List<Map<String, String>> previewTemplates(JavaProperties userEntity) {
        String module = userEntity.getModule();//模块 "generator"
        String moduleFull = userEntity.getModuleFull();//模块 "generator"
        String pack = userEntity.getPack();//包名 "com.syyo"
        // 初始化，获取启动类路劲、模板路劲、模板文件名
        Map<String, Object> map = init(pack, moduleFull, module);
        List<String> templates = (List<String>) map.get("templates");
        // 遍历文件名生成对应的模板
        List<Map<String, String>> list = new ArrayList<>();
        for (String templateName : templates) {
            Map<String, String> previewMap = new HashMap<>();
            // 文件名
            String preview = preview(templateName, userEntity);
            String fileName = isFileName(templateName);
            previewMap.put("name", fileName);
            previewMap.put("content", preview);
            list.add(previewMap);
        }
        return list;
    }

    /**
     * 根据文件名拼接对应的是生成文件的路劲
     *
     * @return
     */
    private static String isFileName(String templateName) {
        String fileName = templateName.substring(0, templateName.lastIndexOf("."));// controller.java.ftl => controller.java
        switch (fileName) {
            case "entity.java":
                return "entity";
            case "export.java":
                return "export";
            case "req.java":
                return "req";
            case "controller.java":
                return "controller";
            case "service.java":
                return "service";
            case "serviceImpl.java":
                return "serviceImpl";
            case "mapper.java":
                return "mapper";
            case "mapper.xml":
                return "mapperXml";
            case "index.vue":
                return "index";
            case "api.js":
                return "api";
            default:
                return "";
        }
    }


    public static void main(String[] args) throws IOException, TemplateException {
        String rootPath = System.getProperty("user.dir");


        String sprit = File.separator;//获取系统的斜杠
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append(rootPath);
        stringBuffer.append(sprit);
        stringBuffer.append("syyo-generator");
        stringBuffer.append(sprit);
        stringBuffer.append("src");
        stringBuffer.append(sprit);
        stringBuffer.append("main");
        stringBuffer.append(sprit);
        stringBuffer.append("resources");
        stringBuffer.append(sprit);
        stringBuffer.append("templates");
        stringBuffer.append(sprit);
        stringBuffer.append("generator");
        stringBuffer.append(sprit);
        System.out.println(stringBuffer.toString());
//        stringBuffer.append(rootPath);
//        stringBuffer.append(sprit);
//        stringBuffer.append("generator");
//        stringBuffer.append(sprit);
//        stringBuffer.append("src");
//        stringBuffer.append(sprit);
//        stringBuffer.append("main");
//        stringBuffer.append(sprit);
//        stringBuffer.append("resources");
//        stringBuffer.append(sprit);
//        stringBuffer.append("temporary");
//        stringBuffer.append(sprit);
//        System.out.println(stringBuffer.toString());

        // 当前项目的地址 G:\syyo\code\java\kaiyuan\syyoAdmin-server
//        String rootPath = System.getProperty("user.dir");
//        String sprit = File.separator;//获取系统的斜杠
//        String modelName = "generator";//模块名
////        String modelName = "generator";//模块名
//        String packageName = "com.syyo"; //包名
//        String packageNames = ""; //转成斜杠 包名  \com\syyo
//        String[] split = packageName.split("\\.");
//        for (String s : split) {
//            packageNames = packageNames + sprit + s;
//        }
        // 当前项目的地址
//        String applicationPath = getApplicationPath(rootPath,modelName,packageNames,sprit);//G:\syyo\code\java\kaiyuan\syyoAdmin-server\admin\src\main\java\\com\syyo\admin\
//        // 模板的路劲
//        String templatePath = getTemplatePath(rootPath, sprit);// G:\syyo\code\java\kaiyuan\syyoAdmin-server\generator\src\main\resources\templates\generator\
//        // 模板文件名
//        Set<String> templates = getTemplates(templatePath);


//        for (String templateName : templates) {
//            // 文件名
//            String fileName = templateName.substring(0, templateName.lastIndexOf("."));
//            String classFilePath = isFileType(fileName, applicationPath, sprit);// 生成后的文件路劲
//
//            createTemplates();
////            userEntity.addField(String.class, "username","username","用户名",true);
////            userEntity.addField(LocalDate.class, "birthday","birthday","生日",false);
////            userEntity.addField(LocalDateTime.class, "addTime","add_time","时间",false);
////            userEntity.addField(Integer.class, "gender","gender","性别",false);
////            userEntity.addField(Integer.class, "age","age","年龄",false);
//            autoCodingJavaEntity(classFilePath, templatePath, templateName, userEntity,fileName);
//
//        }

    }
}